﻿using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Xml.Linq;

namespace FlightToolkit.SimProp
{
    internal class PropertyDef : SymbolDef
    {
        internal PropertyDef(Symbols symbols, NamespaceDef namespaceDef, XElement def)
            : base(namespaceDef, def, Cardinality.Single)
        {
            if (this.Id != Guid.Empty)
            {
                this.Type = symbols.LookupTypeDefByName(def.Attribute("type").Value);
            }
            else
            {
                this.Type = symbols.LookupTypeDefByName("UNDEFINED");
            }

            var xmlioAttrib = def.Attribute("xml_io");
            if (xmlioAttrib != null)
            {
                this.SerializeAsXmlAttribute = xmlioAttrib.Value.Equals("attribute", StringComparison.OrdinalIgnoreCase);
            }
            else
            {
                this.SerializeAsXmlAttribute = false;
            }

            var locAttrib = def.Attribute("localize");
            if (locAttrib != null)
            {
                this.Localize = SymbolDef.LookupLocalizeByName(locAttrib.Value);
            }
            else
            {
                this.Localize = Localize.No;
            }

            switch (this.Type.Type)
            {
                case SimProp.Type.Bool:
                    this.SystemType = typeof(bool);
                    break;
                case SimProp.Type.Byte4:
                    this.SystemType = typeof(byte[]);
                    break;
                case SimProp.Type.Double:
                    this.SystemType = typeof(double);
                    break;
                case SimProp.Type.LLA:
                    this.SystemType = typeof(double[]);
                    break;
                case SimProp.Type.Filetime:
                    this.SystemType = typeof(DateTime);
                    break;
                case SimProp.Type.Float:
                    this.SystemType = typeof(float);
                    break;
                case SimProp.Type.Float2:
                case SimProp.Type.Float3:
                case SimProp.Type.Float4:
                case SimProp.Type.Matrix:
                case SimProp.Type.PBH:
                    this.SystemType = typeof(float[]);
                    break;
                case SimProp.Type.Guid:
                    this.SystemType = typeof(Guid);
                    break;
                case SimProp.Type.Enum:
                    {
                        var enumDef = def.Element("EnumDef");

                        // Define a public enumeration with an underlying type of Integer.
                        EnumBuilder eb = symbols.moduleBuilder.DefineEnum("FlightToolkit.SimProp.Types." + this.namespaceDef.Name + "." + this.Name, TypeAttributes.Public, typeof(int));

                        // Define members
                        int i = 0;
                        if (enumDef != null) // some enums in FSX don't actually have any values
                        {
                            foreach (var enumVal in enumDef.Descendants("EnumVal"))
                            {
                                string valueVame = enumVal.Attribute("name").Value;
                                var xmlName = enumVal.Attribute("xml_name");
                                if (xmlName != null)
                                {
                                    valueVame = xmlName.Value;
                                }

                                eb.DefineLiteral(valueVame, i);
                                i++;
                            }
                        }

                        // Create the type
                        this.SystemType = eb.CreateType();
                    }
                    break;
                case SimProp.Type.Long:
                    this.SystemType = typeof(int);
                    break;
                case SimProp.Type.ULong:
                    this.SystemType = typeof(uint);
                    break;
                case SimProp.Type.Long2:
                case SimProp.Type.Long3:
                case SimProp.Type.Long4:
                    this.SystemType = typeof(int[]);
                    break;
                case SimProp.Type.String:
                    this.SystemType = typeof(string);
                    break;
                default:
                    if (this.Id != Guid.Empty)
                    {
                        throw new Exception("Invalid property type");
                    }
                    this.SystemType = null;
                    break;
            }
        }

        internal bool SerializeAsXmlAttribute { get; private set; }
        internal Localize Localize { get; private set; }
        internal System.Type SystemType { get; private set; }
    }
}
